#include "BlockFileManager.h"

BlockFileManager::BlockFileManager(int blockSize, string path) {
	this->blockSize = blockSize;
	this->filePath = path + "File.dat";
	this->freeBlocksFilePath = path + "freeBlocks.dat";
	this->openFile(Read);
}
/*
 * Apertura de archivos
 */
void BlockFileManager::openFile(OpenMode mode) {

	char* fileNameStartingPosition = &(this->filePath[0]);
	char* freeBlocks_startingPosition = &(this->freeBlocksFilePath[0]);

	this->blockFile.open(fileNameStartingPosition,ios::in | ios::out | ios::binary);
	if (!(blockFile.good())) {
		blockFile.open(fileNameStartingPosition,ios::in | ios::out | ios::trunc | ios::binary);
	}
	this->freeBlocksFile.open(freeBlocks_startingPosition,ios::out | ios::in | ios::binary);
	if (!(freeBlocksFile.good())) {
		freeBlocksFile.open(freeBlocks_startingPosition,ios::in | ios::out | ios::trunc| ios::binary);
		this->cantFreeBlocks = 0;
		this->freeBlocksFile.write((char*) &this->cantFreeBlocks, sizeof(int));
	} else {
		this->freeBlocksFile.read((char*) &this->cantFreeBlocks, sizeof(int));
	}

}
int BlockFileManager::writeNewBlock(char* buffer) {
	//write recordLength and then the book data
	int freeBlock = this-> getFreeBlocks();
	if (freeBlock != (-1)) {
		this->updateBlock(buffer, freeBlock);
		return freeBlock;
	} else {
		this-> blockFile.seekp(0, ios::end);
		this-> blockFile.write(buffer, this-> blockSize);
		this-> blockFile.clear();
		return (this->getNumberOfBlocks()-1);
	}

}

void BlockFileManager::updateBlock(char* buffer, int blockNumber) {
	int blockPosition = (this->blockSize) * blockNumber;
	this-> blockFile.seekp(blockPosition, ios::beg);
	this-> blockFile.write(buffer, this-> blockSize);
	this-> blockFile.clear();
}

char* BlockFileManager::readBlock(int blockNumber) {
	if (!(this->isFreeBlock(blockNumber))) {
		int blockPosition = (this->blockSize) * blockNumber;
		this-> blockFile.seekg(blockPosition, ios::beg);
		char* buffer = new char[this->blockSize];//ver si es necesario que sea dinamico
		this-> blockFile.read(buffer, this-> blockSize);
		this->blockFile.clear();
		return buffer;
	} else {
		return NULL;
	}
}
/*
 * Devuelve un bloque libre,
 *
 */
int BlockFileManager::getFreeBlocks() {
	int numBlock = -1;
	if (this->cantFreeBlocks != 0) {
		this->freeBlocksFile.clear();
		//	do {
		//			this->freeBlocksFile.get((char*)&numBlock, 4);
		//		} while ((numBlock == -1) && !(this->freeBlocksFile.eof()));
		//		this->freeBlocksFile.clear();
		int freeBlockPosition = (this->cantFreeBlocks * 4);
		this->freeBlocksFile.seekg(freeBlockPosition, ios::beg);
		this->freeBlocksFile.read((char*) &numBlock, sizeof(int));
		// Se modifico la cantidad de bloques libre, trunco el archivo, actualizo el campo de control de bloqueLibres
		this->freeBlocksFile.clear();
		this->cantFreeBlocks = this->cantFreeBlocks - 1;
		this->updateCantFreeBlocks();
	}
	return numBlock;
}

//return -1 if a free block dosen't exist , otherwise it returns the numb
bool BlockFileManager::isFreeBlock(int blockNumber) {
	bool founded = false;
	int numBlock = -1;
	if (this->cantFreeBlocks != 0) {
		int cant = this->cantFreeBlocks;
		//TODO:: Chequea que el bloque no este en el archivo de bloques libres
		this->freeBlocksFile.seekg(sizeof(int), ios::beg);
		while ((cant != 0) && !founded) {
			this->freeBlocksFile.read((char*) &numBlock, sizeof(int));
			if (numBlock == blockNumber) {
				founded = true;
			}
			cant--;
		}
	}
	this->freeBlocksFile.clear();
	return founded;
}

void BlockFileManager::deleteBlock(int blockNumber) {
	//Por si se intenta liberar dos veces el mismo bloque
	if (!(this->isFreeBlock(blockNumber))) {
		this->addFreeBlock(blockNumber);
	}
}
/*
 * 	Agrega el bloque al archivo de bloques libres
 */
void BlockFileManager::addFreeBlock(int blockNumber) {
	int freeBlockPosition = (this->cantFreeBlocks * sizeof(int)) + sizeof(int);
	this->freeBlocksFile.seekp(freeBlockPosition, ios::beg);
	this->freeBlocksFile.write((char*) &blockNumber, sizeof(int));
	this->cantFreeBlocks = this->cantFreeBlocks + 1;
	this->updateCantFreeBlocks();

}

int BlockFileManager::getNumberOfBlocks() {
	// Calcular el número de elementos almacenados en un fichero ir al final del fichero
	this->blockFile.seekg(0, ios::end);
	// leer la posición actual
	int pos = this->blockFile.tellg();
	// El número de registros es el tamaño en bytes dividido entre el tamaño del registro:
	this->blockFile.clear();
	int toReturn;
	if (pos != 0) {
		  toReturn = (pos / this->blockSize);
	}else{
		toReturn = 0;
	}
	return toReturn;
}
vector<int> * BlockFileManager::getVecFreeBlocks(){
	vector<int> * vec = new vector<int>;
	this->freeBlocksFile.seekg(sizeof(int),ios::beg);
	for(int i =0; i<this->cantFreeBlocks; i++){
		int numBlock;
		this->freeBlocksFile.read((char*)&numBlock,sizeof(int));
		vec->push_back(numBlock);
	}
	this->freeBlocksFile.clear();
	return vec;

}
void BlockFileManager::updateCantFreeBlocks(){
	this->freeBlocksFile.seekp(0, ios::beg);
	this->freeBlocksFile.write((char*) &this->cantFreeBlocks, sizeof(int));
	this->freeBlocksFile.clear();
}

int BlockFileManager::getBlockSize(){
	return this->blockSize;

}
BlockFileManager::~BlockFileManager() {
	this->blockFile.close();
	this->freeBlocksFile.close();

}
